﻿using System;
using Xenta.Attributes;
using Xenta.Security;
using Xenta.Utils;

namespace Xenta.Operations
{
    /// <summary>
    /// Exposes the operation processor interface.
    /// </summary>
    /// <remarks>
    /// The operation processor is a core part of the framework. 
    /// It allows the components of system, including API services, 
    /// to execute operations.
    /// </remarks>
    public interface IOperationProcessor : IDependency, IDisposable
    {
        /// <summary>
        /// Initializes the operation processor.
        /// </summary>
        /// <param name="ctx">The working context.</param>
        void Initialize(IWorkingContext ctx);

        /// <summary>
        /// Commits all previously executed opertaions.
        /// </summary>
        void Commit();

        /// <summary>
        /// Rollbacks all previosly executed opertaions.
        /// </summary>
        void Rollback();

        /// <summary>
        /// Executes an operation.
        /// </summary>
        /// <typeparam name="TOperation">The type of operation.</typeparam>
        /// <param name="operation">The operation object.</param>
        /// <returns>The processed operation.</returns>
        TOperation Execute<TOperation>(TOperation operation)
            where TOperation : IOperation;
    }

    /// <summary>
    /// The IOperationProcessor implementation.
    /// </summary>
    [Dependency]
    public class OperationProcessorImpl : IOperationProcessor
    {
        #region Fields

        private readonly IInfrastructure _infrastructure;
        private UnitOfWorkLeasehold _leasehold;
        private IWorkingContext _ctx;
        private bool _initialized;

        #endregion

        #region Ctors

        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <param name="infrastructure">The infrastructure instance.</param>
        public OperationProcessorImpl(IInfrastructure infrastructure)
        {
            _infrastructure = infrastructure;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Initializes the operation processor.
        /// </summary>
        /// <param name="ctx">The working context.</param>
        public void Initialize(IWorkingContext ctx)
        {
            if(ctx == null)
                throw new ArgumentNullException("ctx");
            if(_initialized)
                throw Err.Of(this).Message("Is already initialized").Ex();

            _leasehold = _infrastructure.UoWPool.Lease(ctx.CID);
            _leasehold.UoW.Infrastructure = _infrastructure;
            _ctx = ctx;
            _initialized = true;
        }

        /// <summary>
        /// Commits all previously executed opertaions.
        /// </summary>
        public void Commit()
        {
            if(!_initialized)
                throw Err.Of(this).Message("Is not initialized").Ex();
            _leasehold.UoW.Commit();
        }

        /// <summary>
        /// Rollbacks all previosly executed opertaions.
        /// </summary>
        public void Rollback()
        {
            if(!_initialized)
                throw Err.Of(this).Message("Is not initialized").Ex();
            _leasehold.UoW.Rollback();
        }

        /// <summary>
        /// Executes an operation.
        /// </summary>
        /// <typeparam name="TOperation">The type of operation.</typeparam>
        /// <param name="operation">The operation object.</param>
        /// <returns>The processed operation.</returns>
        public TOperation Execute<TOperation>(TOperation operation) 
            where TOperation : IOperation
        {
            if(!_initialized)
                throw Err.Of(this).NotInit().Ex();
            if(operation is ISecuredOperation)
            {
                var so = operation as ISecuredOperation;
                bool allowed = _infrastructure.SecurityAuditor
                    .Audit(_ctx, _leasehold.UoW, so);
                if(!allowed)
                    throw Err.Of<ISecuredOperation>().NotAuthorized().Ex();
            }
            return _leasehold.UoW.Execute(operation);
        }

        /// <summary>
        /// Returns the leasehold to a pool.
        /// </summary>
        public void Dispose()
        {
            if(!_initialized) 
                return;
            _leasehold.UoW.Infrastructure = null;
            _infrastructure.UoWPool.Return(_leasehold);
            _leasehold = null;
            _ctx = null;
        }

        #endregion
    }
}
